home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 176-200 / disk_197 / stevie / normal.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  35KB  |  1,775 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. /*
  10.  * This file contains the main routine for processing characters in command
  11.  * mode as well as routines for handling the operators. 
  12.  */
  13.  
  14. #include "stevie.h"
  15.  
  16. static void
  17. doshift(), dodelete(), doput(), dochange();
  18. static void
  19.                 startinsert();
  20. static          bool_t
  21.                 dojoin();
  22. static          bool_t
  23.                 doyank();
  24.  
  25. /*
  26.  * Macro evaluates true if char 'c' is a valid identifier character 
  27.  */
  28. #define    IDCHAR(c)    (isalpha(c) || isdigit(c) || (c) == '_')
  29.  
  30. /*
  31.  * Operators 
  32.  */
  33. #define    NOP    0        /* no pending operation */
  34. #define    DELETE    1
  35. #define    YANK    2
  36. #define    CHANGE    3
  37. #define    LSHIFT    4
  38. #define    RSHIFT    5
  39.  
  40. #define    CLEAROP    (operator = NOP)/* clear any pending operator */
  41.  
  42. static int      operator = NOP;    /* current pending operator */
  43.  
  44. /*
  45.  * When a cursor motion command is made, it is marked as being a character or
  46.  * line oriented motion. Then, if an operator is in effect, the operation
  47.  * becomes character or line oriented accordingly. 
  48.  *
  49.  * Character motions are marked as being inclusive or not. Most char. motions
  50.  * are inclusive, but some (e.g. 'w') are not. 
  51.  *
  52.  * Generally speaking, every command in normal() should either clear any pending
  53.  * operator (with CLEAROP), or set the motion type variable. 
  54.  */
  55.  
  56. /*
  57.  * Motion types 
  58.  */
  59. #define    MBAD    (-1)        /* 'bad' motion type marks unusable yank buf */
  60. #define    MCHAR    0
  61. #define    MLINE    1
  62.  
  63. static int      mtype;        /* type of the current cursor motion */
  64. static bool_t   mincl;        /* true if char motion is inclusive */
  65. static int      ybtype = MBAD;
  66. static int      ybcrossline = FALSE;
  67.  
  68. static LPtr     startop;    /* cursor pos. at start of operator */
  69.  
  70. /*
  71.  * Operators can have counts either before the operator, or between the
  72.  * operator and the following cursor motion as in: 
  73.  *
  74.  * d3w or 3dw 
  75.  *
  76.  * If a count is given before the operator, it is saved in opnum. If normal() is
  77.  * called with a pending operator, the count in opnum (if present) overrides
  78.  * any count that came later. 
  79.  */
  80. static int      opnum = 0;
  81.  
  82. #define    DEFAULT1(x)    (((x) == 0) ? 1 : (x))
  83.  
  84. /*
  85.  * normal 
  86.  *
  87.  * Execute a command in normal mode. 
  88.  */
  89.  
  90. void
  91. normal(c)
  92.     char            c;
  93. {
  94.     char           *p;
  95.     int             n;
  96.     int             nn;
  97.     bool_t          flag = FALSE;
  98.     int             type = 0;    /* used in some operations to modify type */
  99.     int             dir = FORWARD;    /* search direction */
  100.     char            nchar = NUL;
  101.     bool_t          finish_op;
  102.     LPtr            temp_Curschar;
  103.  
  104.     last_command = NUL;
  105.     /*
  106.      * If there is an operator pending, then the command we take this time
  107.      * will terminate it. Finish_op tells us to finish the operation before
  108.      * returning this time (unless the operation was cancelled). 
  109.      */
  110.     finish_op = (operator != NOP);
  111.  
  112.     /*
  113.      * If we're in the middle of an operator AND we had a count before the
  114.      * operator, then that count overrides the current value of Prenum. What
  115.      * this means effectively, is that commands like "3dw" get turned into
  116.      * "d3w" which makes things fall into place pretty neatly. 
  117.      */
  118.     if (finish_op) {
  119.     if (opnum != 0)
  120.         Prenum = opnum;
  121.     } else
  122.     opnum = 0;
  123.  
  124.     switch (c) {
  125.  
  126.       case K_HELP:
  127.     CLEAROP;
  128.     if (help()) {
  129.         screenclear();
  130.         updateNextscreen(NOT_VALID);
  131.     }
  132.     break;
  133.  
  134.       case CTRL('L'):
  135.     CLEAROP;
  136.     screenclear();
  137.     updateNextscreen(NOT_VALID);
  138.     break;
  139.  
  140.       case CTRL('D'):
  141.     CLEAROP;
  142.     if (Prenum)
  143.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  144.     scrollup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  145.     onedown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  146.     updateNextscreen(VALID);
  147.     break;
  148.  
  149.       case CTRL('U'):
  150.     CLEAROP;
  151.     if (Prenum)
  152.         P(P_SS) = (Prenum > Rows - 1) ? Rows - 1 : Prenum;
  153.     scrolldown((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  154.     oneup((P(P_SS) < Rows) ? P(P_SS) : Rows - 1);
  155.     updateNextscreen(VALID);
  156.     break;
  157.  
  158.       case CTRL('F'):
  159.     CLEAROP;
  160.     if (nextline(Topchar) == NULL) {
  161.         beep();
  162.         break;
  163.     }
  164.     screenclear();
  165.     Prenum = DEFAULT1(Prenum);
  166.     while (Prenum > 0) {
  167.         *Curschar = *prevline(Botchar);
  168.         *Topchar = *Curschar;
  169.         Topchar->index = 0;
  170.         updateNextscreen(VALID);
  171.         Prenum--;
  172.     }
  173.     beginline(TRUE);
  174.     break;
  175.  
  176.       case CTRL('B'):
  177.     CLEAROP;
  178.     if (prevline(Topchar) == NULL) {
  179.         beep();
  180.         break;
  181.     }
  182.     screenclear();
  183.     Prenum = DEFAULT1(Prenum);
  184.     while (Prenum > 0) {
  185.         *Curschar = *Topchar;
  186.         n = Rows - 1;
  187.         {
  188.         LPtr           *lp = Curschar;
  189.         int             l = 0;
  190.  
  191.         while ((l < n) && (lp != NULL)) {
  192.             l += plines(lp);
  193.             *Topchar = *lp;
  194.             lp = prevline(lp);
  195.         }
  196.         }
  197.         Topchar->index = 0;
  198.         Prenum--;
  199.     }
  200.     beginline(TRUE);
  201.     updateNextscreen(VALID);
  202.     break;
  203.  
  204.       case CTRL('E'):
  205.     CLEAROP;
  206.     scrollup(DEFAULT1(Prenum));
  207.     updateNextscreen(VALID);
  208.     break;
  209.  
  210.       case CTRL('Y'):
  211.     CLEAROP;
  212.     scrolldown(DEFAULT1(Prenum));
  213.     updateNextscreen(VALID);
  214.     break;
  215.  
  216.       case 'z':
  217.     CLEAROP;
  218.     switch (vgetc()) {
  219.       case NL:        /* put Curschar at top of screen */
  220.       case CR:
  221.         *Topchar = *Curschar;
  222.         Topchar->index = 0;
  223.         updateNextscreen(VALID);
  224.         break;
  225.  
  226.       case '.':        /* put Curschar in middle of screen */
  227.         n = Rows / 2;
  228.         goto dozcmd;
  229.  
  230.       case '-':        /* put Curschar at bottom of screen */
  231.         n = Rows - 1;
  232.         /* FALLTHROUGH */
  233.  
  234.     dozcmd:
  235.         {
  236.         register LPtr  *lp = Curschar;
  237.         register int    l = 0;
  238.  
  239.         while ((l < n) && (lp != NULL)) {
  240.             l += plines(lp);
  241.             *Topchar = *lp;
  242.             lp = prevline(lp);
  243.         }
  244.         }
  245.         Topchar->index = 0;
  246.         updateNextscreen(VALID);
  247.         break;
  248.  
  249.       default:
  250.         beep();
  251.     }
  252.     break;
  253.  
  254.       case CTRL('G'):
  255.     CLEAROP;
  256.     fileinfo();
  257.     break;
  258.  
  259.       case 'G':
  260.     mtype = MLINE;
  261.     *Curschar = *gotoline(Prenum);
  262.     if (!UndoInProgress)
  263.         beginline(TRUE);
  264.     break;
  265.  
  266.       case 'H':
  267.     mtype = MLINE;
  268.     *Curschar = *Topchar;
  269.     for (n = Prenum; n && onedown(1); n--);
  270.     beginline(TRUE);
  271.     break;
  272.  
  273.       case 'M':
  274.     mtype = MLINE;
  275.     *Curschar = *Topchar;
  276.     for (n = 0; n < Rows / 2 && onedown(1); n++);
  277.     beginline(TRUE);
  278.     break;
  279.  
  280.       case 'L':
  281.     mtype = MLINE;
  282.     *Curschar = *prevline(Botchar);
  283.     for (n = Prenum; n && oneup(1); n--);
  284.     beginline(TRUE);
  285.     break;
  286.  
  287.       case 'l':
  288.       case K_RARROW:
  289.       case ' ':
  290.     mtype = MCHAR;
  291.     mincl = FALSE;
  292.     n = DEFAULT1(Prenum);
  293.     while (n--) {
  294.         if (!oneright()) {
  295.         if (operator != DELETE && operator != CHANGE) {
  296.             beep();
  297.         } else {
  298.             if (lineempty(Curschar)) {
  299.             CLEAROP;
  300.             beep();
  301.             } else {
  302.             mincl = TRUE;
  303.             }
  304.         }
  305.         break;
  306.         }
  307.     }
  308.     set_want_col = TRUE;
  309.     break;
  310.  
  311.       case 'h':
  312.       case K_LARROW:
  313.       case CTRL('H'):
  314.     mtype = MCHAR;
  315.     mincl = FALSE;
  316.     Prenum = DEFAULT1(Prenum);
  317.     n = Prenum;
  318.     while (n--) {
  319.         if (!oneleft()) {
  320.         if (operator != DELETE && operator != CHANGE) {
  321.             beep();
  322.         } else if (Prenum == 1) {
  323.             CLEAROP;
  324.             beep();
  325.         }
  326.         break;
  327.         }
  328.     }
  329.     set_want_col = TRUE;
  330.     break;
  331.  
  332.       case '-':
  333.     flag = TRUE;
  334.     /* FALLTHROUGH */
  335.  
  336.       case 'k':
  337.       case K_UARROW:
  338.       case CTRL('P'):
  339.     mtype = MLINE;
  340.     if (!oneup(DEFAULT1(Prenum))) {
  341.         CLEAROP;
  342.         beep();
  343.     } else if (flag)
  344.         beginline(TRUE);
  345.     break;
  346.  
  347.       case '+':
  348.       case CR:
  349.       case NL:
  350.     flag = TRUE;
  351.     /* FALLTHROUGH */
  352.  
  353.       case 'j':
  354.       case K_DARROW:
  355.       case CTRL('N'):
  356.     mtype = MLINE;
  357.     if (!onedown(DEFAULT1(Prenum))) {
  358.         CLEAROP;
  359.         beep();
  360.     } else if (flag)
  361.         beginline(TRUE);
  362.     break;
  363.  
  364.     /*
  365.      * This is a strange motion command that helps make operators more
  366.      * logical. It is actually implemented, but not documented in the
  367.      * real 'vi'. This motion command actually refers to "the current
  368.      * line". Commands like "dd" and "yy" are really an alternate form of
  369.      * "d_" and "y_". It does accept a count, so "d3_" works to delete 3
  370.      * lines. 
  371.      */
  372.       case '_':
  373. lineop:
  374.     mtype = MLINE;
  375.     if (!onedown(DEFAULT1(Prenum) - 1)) {
  376.         CLEAROP;
  377.         beep();
  378.     } else
  379.         beginline(TRUE);
  380.     break;
  381.  
  382.       case '|':
  383.     mtype = MCHAR;
  384.     mincl = TRUE;
  385.     beginline(FALSE);
  386.     if (Prenum > 0)
  387.         *Curschar = *coladvance(Curschar, Prenum - 1);
  388.     Curswant = Prenum - 1;
  389.     break;
  390.  
  391.       case CTRL(']'):        /* :ta to current identifier */
  392.     CLEAROP;
  393.     {
  394.         char            ch;
  395.         LPtr            save;
  396.  
  397.         save = *Curschar;
  398.         /*
  399.          * First back up to start of identifier. This doesn't match the
  400.          * real vi but I like it a little better and it shouldn't bother
  401.          * anyone. 
  402.          */
  403.         ch = gchar(Curschar);
  404.         while (IDCHAR(ch)) {
  405.         if (!oneleft())
  406.             break;
  407.         ch = gchar(Curschar);
  408.         }
  409.         if (!IDCHAR(ch))
  410.         oneright();
  411.  
  412.         stuffReadbuff(":ta ");
  413.         /*
  414.          * Now grab the chars in the identifier 
  415.          */
  416.         ch = gchar(Curschar);
  417.         while (IDCHAR(ch)) {
  418.         stuffReadbuff(mkstr(ch));
  419.         if (!oneright())
  420.             break;
  421.         ch = gchar(Curschar);
  422.         }
  423.         stuffReadbuff("\n");
  424.  
  425.         *Curschar = save;    /* restore, in case of error */
  426.     }
  427.     break;
  428.  
  429.       case '%':
  430.     mtype = MCHAR;
  431.     mincl = TRUE;
  432.     {
  433.         LPtr           *pos;
  434.  
  435.         if ((pos = showmatch()) == NULL) {
  436.         CLEAROP;
  437.         beep();
  438.         } else {
  439.         setpcmark();
  440.         *Curschar = *pos;
  441.         set_want_col = TRUE;
  442.         }
  443.     }
  444.     break;
  445.  
  446.     /*
  447.      * Word Motions 
  448.      */
  449.  
  450.       case 'B':
  451.     type = 1;
  452.     /* FALLTHROUGH */
  453.  
  454.       case 'b':
  455.     mtype = MCHAR;
  456.     mincl = FALSE;
  457.     set_want_col = TRUE;
  458.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  459.         LPtr           *pos;
  460.  
  461.         if ((Curschar->linep->prev == Filetop->linep)
  462.         && (Curschar->index == 0)) {
  463.         CLEAROP;
  464.         beep();
  465.         break;
  466.         }
  467.         pos = bck_word(Curschar, type);
  468.         if (pos == NULL) {
  469.         CLEAROP;
  470.         beep();
  471.         *Curschar = *gotoline(1);    /* goto top of file */
  472.         } else
  473.         *Curschar = *pos;
  474.     }
  475.     break;
  476.  
  477.       case 'W':
  478.     type = 1;
  479.     /* FALLTHROUGH */
  480.  
  481.       case 'w':
  482.     /*
  483.      * This is a little strange. To match what the real vi does, we
  484.      * effectively map 'cw' to 'ce', and 'cW' to 'cE'. This seems
  485.      * impolite at first, but it's really more what we mean when we say
  486.      * 'cw'. 
  487.      */
  488.     if (operator == CHANGE)
  489.         goto doecmd;
  490.  
  491.     mtype = MCHAR;
  492.     mincl = FALSE;
  493.     set_want_col = TRUE;
  494.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  495.         LPtr           *pos;
  496.  
  497.         if ((pos = fwd_word(Curschar, type)) == NULL) {
  498.         CLEAROP;
  499.         beep();
  500.         break;
  501.         } else
  502.         *Curschar = *pos;
  503.     }
  504.     break;
  505.  
  506.       case 'E':
  507.     type = 1;
  508.     /* FALLTHROUGH */
  509.  
  510.       case 'e':
  511. doecmd:
  512.     mtype = MCHAR;
  513.     mincl = TRUE;
  514.     set_want_col = TRUE;
  515.     for (n = DEFAULT1(Prenum); n > 0; n--) {
  516.         LPtr           *pos;
  517.  
  518.         if ((pos = end_word(Curschar, type)) == NULL) {
  519.         CLEAROP;
  520.         beep();
  521.         break;
  522.         } else
  523.         *Curschar = *pos;
  524.     }
  525.     break;
  526.  
  527.       case '$':
  528.     mtype = MCHAR;
  529.     mincl = TRUE;
  530.     while (oneright());
  531.     Curswant = 999;        /* so we stay at the end */
  532.     break;
  533.  
  534.       case '^':
  535.     flag = TRUE;
  536.     /* FALLTHROUGH */
  537.  
  538.       case '0':
  539.     mtype = MCHAR;
  540.     mincl = TRUE;
  541.     beginline(flag);
  542.     break;
  543.  
  544.       case 'A':
  545.     set_want_col = TRUE;
  546.     while (oneright());
  547.     ResetBuffers();
  548.     AppendToRedobuff("A");
  549.     goto doAPPENDcmd;
  550.  
  551.       case 'a':
  552.     ResetBuffers();
  553.     AppendToRedobuff("a");
  554.  
  555. doAPPENDcmd:
  556.     CLEAROP;
  557.     /* Works just like an 'i'nsert on the next character. */
  558.     n = RowNumber(Curschar);
  559.     AppendPositionToUndoUndobuff(Curschar->index, n);
  560.     AppendToUndoUndobuff("a");
  561.  
  562.     if (!lineempty(Curschar))
  563.         inc(Curschar);
  564.  
  565.     n = RowNumber(Curschar);
  566.     AppendPositionToUndobuff(Curschar->index, n);
  567.  
  568.     startinsert(FALSE);
  569.     break;
  570.  
  571.       case 'I':
  572.     beginline(TRUE);
  573.     ResetBuffers();
  574.     AppendToRedobuff("I");
  575.     goto doINSERTcmd;
  576.     /* FALLTHROUGH */
  577.  
  578.       case 'i':
  579.       case K_INSERT:
  580.     ResetBuffers();
  581.     AppendToRedobuff("i");
  582.  
  583. doINSERTcmd:
  584.     CLEAROP;
  585.  
  586.     n = RowNumber(Curschar);
  587.     AppendPositionToUndobuff(Curschar->index, n);
  588.     AppendPositionToUndoUndobuff(Curschar->index, n);
  589.     AppendToUndoUndobuff("i");
  590.  
  591.     startinsert(FALSE);
  592.     break;
  593.  
  594.       case 'o':
  595.     CLEAROP;
  596.     ResetBuffers();
  597.  
  598.     n = RowNumber(Curschar);
  599.     AppendToRedobuff("o");
  600.     AppendPositionToUndobuff(Curschar->index, n);
  601.     AppendPositionToUndoUndobuff(Curschar->index, n);
  602.     AppendToUndoUndobuff("o");
  603.  
  604.     if (OpenForward(!RedrawingDisabled))
  605.         startinsert(TRUE);
  606.  
  607.     last_command = 'o';
  608.     break;
  609.  
  610.       case 'O':
  611.     CLEAROP;
  612.     ResetBuffers();
  613.  
  614.     n = RowNumber(Curschar);
  615.     AppendToRedobuff("O");
  616.     AppendPositionToUndobuff(Curschar->index, n);
  617.     AppendPositionToUndoUndobuff(Curschar->index, n);
  618.     AppendToUndoUndobuff("O");
  619.  
  620.     if (OpenBackward(!RedrawingDisabled))
  621.         startinsert(TRUE);
  622.  
  623.     last_command = 'O';
  624.     break;
  625.  
  626.       case 'd':
  627.     if (operator == DELETE)    /* handle 'dd' */
  628.         goto lineop;
  629.     if (Prenum != 0)
  630.         opnum = Prenum;
  631.     startop = *Curschar;
  632.     operator = DELETE;
  633.     break;
  634.  
  635.     /*
  636.      * Some convenient abbreviations... 
  637.      */
  638.  
  639.       case 'x':
  640.     if (Prenum)
  641.         stuffnumReadbuff(Prenum);
  642.     stuffReadbuff("dl");
  643.     break;
  644.  
  645.       case 'X':
  646.     if (Prenum)
  647.         stuffnumReadbuff(Prenum);
  648.     stuffReadbuff("dh");
  649.     break;
  650.  
  651.       case 'D':
  652.     stuffReadbuff("d$");
  653.     break;
  654.  
  655.       case 'Y':
  656.     if (Prenum)
  657.         stuffnumReadbuff(Prenum);
  658.     stuffReadbuff("yy");
  659.     break;
  660.  
  661.       case 'C':
  662.     stuffReadbuff("c$");
  663.     break;
  664.  
  665.       case 'c':
  666.     if (operator == CHANGE) {    /* handle 'cc' */
  667.         CLEAROP;
  668.         stuffReadbuff("0c$");
  669.         break;
  670.     }
  671.     if (Prenum != 0)
  672.         opnum = Prenum;
  673.     startop = *Curschar;
  674.     operator = CHANGE;
  675.     break;
  676.  
  677.       case 'y':
  678.     if (operator == YANK)    /* handle 'yy' */
  679.         goto lineop;
  680.     if (Prenum != 0)
  681.         opnum = Prenum;
  682.     startop = *Curschar;
  683.     operator = YANK;
  684.     break;
  685.  
  686.       case ENABLE_REDRAWING:
  687.     RedrawingDisabled = FALSE;
  688.     updateNextscreen(NOT_VALID);
  689.     break;
  690.  
  691.       case 'p':
  692.     if (Yankbuffptr != NULL) {
  693.         doput(FORWARD);
  694.  
  695.         stuffReadbuff(ENABLE_REDRAWING_STR);
  696.         RedrawingDisabled = TRUE;
  697.     } else
  698.         beep();
  699.     break;
  700.  
  701.       case 'P':
  702.     if (Yankbuffptr != NULL) {
  703.         doput(BACKWARD);
  704.  
  705.         stuffReadbuff(ENABLE_REDRAWING_STR);
  706.         RedrawingDisabled = TRUE;
  707.     } else
  708.         beep();
  709.     break;
  710.  
  711.       case '>':
  712.     if (operator == RSHIFT)    /* handle >> */
  713.         goto lineop;
  714.     if (operator == LSHIFT) {
  715.         CLEAROP;
  716.         beep();
  717.         break;
  718.     }
  719.     if (Prenum != 0)
  720.         opnum = Prenum;
  721.     startop = *Curschar;    /* save current position */
  722.     operator = RSHIFT;
  723.     break;
  724.  
  725.       case '<':
  726.     if (operator == LSHIFT)    /* handle << */
  727.         goto lineop;
  728.     if (operator == RSHIFT) {
  729.         CLEAROP;
  730.         beep();
  731.         break;
  732.     }
  733.     if (Prenum != 0)
  734.         opnum = Prenum;
  735.     startop = *Curschar;    /* save current position */
  736.     operator = LSHIFT;
  737.     break;
  738.  
  739.       case 's':        /* substitute characters */
  740.     if (Prenum)
  741.         stuffnumReadbuff(Prenum);
  742.     stuffReadbuff("cl");
  743.     break;
  744.  
  745.       case '?':
  746.       case '/':
  747.       case ':':
  748.     CLEAROP;
  749.     readcmdline(c, (char *) NULL);
  750.     break;
  751.  
  752.       case 'n':
  753.     mtype = MCHAR;
  754.     mincl = FALSE;
  755.     set_want_col = TRUE;
  756.     if (!repsearch(0)) {
  757.         CLEAROP;
  758.         beep();
  759.     }
  760.     break;
  761.  
  762.       case 'N':
  763.     mtype = MCHAR;
  764.     mincl = FALSE;
  765.     set_want_col = TRUE;
  766.     if (!repsearch(1)) {
  767.         CLEAROP;
  768.         beep();
  769.     }
  770.     break;
  771.  
  772.     /*
  773.      * Character searches 
  774.      */
  775.       case 'T':
  776.     dir = BACKWARD;
  777.     /* FALLTHROUGH */
  778.  
  779.       case 't':
  780.     type = 1;
  781.     goto docsearch;
  782.  
  783.       case 'F':
  784.     dir = BACKWARD;
  785.     /* FALLTHROUGH */
  786.  
  787.       case 'f':
  788. docsearch:
  789.     mtype = MCHAR;
  790.     mincl = TRUE;
  791.     set_want_col = TRUE;
  792.     if ((nchar = vgetc()) == ESC)    /* search char */
  793.         break;
  794.     if (!searchc(nchar, dir, type)) {
  795.         CLEAROP;
  796.         beep();
  797.     }
  798.     break;
  799.  
  800.       case ',':
  801.     flag = 1;
  802.     /* FALLTHROUGH */
  803.  
  804.       case ';':
  805.     mtype = MCHAR;
  806.     mincl = TRUE;
  807.     set_want_col = TRUE;
  808.     if (!crepsearch(flag)) {
  809.         CLEAROP;
  810.         beep();
  811.     }
  812.     break;
  813.  
  814.     /*
  815.      * Function searches 
  816.      */
  817.  
  818.       case '[':
  819.     dir = BACKWARD;
  820.     /* FALLTHROUGH */
  821.  
  822.       case ']':
  823.     mtype = MLINE;
  824.     set_want_col = TRUE;
  825.     if (vgetc() != c) {
  826.         CLEAROP;
  827.         beep();
  828.         break;
  829.     }
  830.     if (!findfunc(dir)) {
  831.         CLEAROP;
  832.         beep();
  833.     }
  834.     break;
  835.  
  836.     /*
  837.      * Marks 
  838.      */
  839.  
  840.       case 'm':
  841.     CLEAROP;
  842.     if (!setmark(vgetc()))
  843.         beep();
  844.     break;
  845.  
  846.       case '\'':
  847.     flag = TRUE;
  848.     /* FALLTHROUGH */
  849.  
  850.       case '`':
  851.     {
  852.         LPtr            mtmp, *mark = getmark(vgetc());
  853.  
  854.         if (mark == NULL) {
  855.         CLEAROP;
  856.         beep();
  857.         } else {
  858.         mtmp = *mark;
  859.         setpcmark();
  860.         *Curschar = mtmp;
  861.         if (flag)
  862.             beginline(TRUE);
  863.         }
  864.         mtype = flag ? MLINE : MCHAR;
  865.         mincl = TRUE;    /* ignored if not MCHAR */
  866.         set_want_col = TRUE;
  867.     }
  868.     break;
  869.  
  870.       case 'r':
  871.     CLEAROP;
  872.     if (lineempty(Curschar)) {    /* Nothing to replace */
  873.         beep();
  874.         break;
  875.     }
  876.     if ((nchar = vgetc()) == ESC)
  877.         break;
  878.  
  879.     Prenum = DEFAULT1(Prenum);
  880.     n = strlen(Curschar->linep->s) - Curschar->index;
  881.     if (n < Prenum) {
  882.         beep();
  883.         break;
  884.     }
  885.     ResetBuffers();
  886.  
  887.     nn = RowNumber(Curschar);
  888.     AppendPositionToUndobuff(Curschar->index, nn);
  889.     AppendPositionToUndoUndobuff(Curschar->index, nn);
  890.  
  891.     while (Prenum > 0) {
  892.         AppendToRedobuff("r");
  893.         AppendToRedobuff(mkstr(nchar));
  894.  
  895.         AppendToUndobuff("r");
  896.         AppendToUndobuff(mkstr(gchar(Curschar)));
  897.  
  898.         AppendToUndoUndobuff("r");
  899.         AppendToUndoUndobuff(mkstr(nchar));
  900.  
  901.         pchar(Curschar, nchar);    /* Change current character. */
  902.  
  903.         if (Prenum > 1) {
  904.         oneright();
  905.         AppendToRedobuff("l");
  906.         AppendToUndobuff("l");
  907.         AppendToUndoUndobuff("l");
  908.         }
  909.         Prenum--;
  910.     }
  911.  
  912.     CHANGED;
  913.     updateline();
  914.     break;
  915.  
  916.       case '~':        /* swap case */
  917.     CLEAROP;
  918.     if (lineempty(Curschar)) {
  919.         beep();
  920.         break;
  921.     }
  922.     ResetBuffers();
  923.  
  924.     n = RowNumber(Curschar);
  925.     AppendPositionToUndobuff(Curschar->index, n);
  926.     AppendPositionToUndoUndobuff(Curschar->index, n);
  927.  
  928.     Prenum = DEFAULT1(Prenum);
  929.     if (Prenum > 0) {
  930.         AppendNumberToRedobuff(Prenum);
  931.         AppendNumberToUndobuff(Prenum);
  932.         AppendNumberToUndoUndobuff(Prenum);
  933.     }
  934.     AppendToRedobuff("~");
  935.     AppendToUndobuff("~");
  936.     AppendToUndoUndobuff("~");
  937.  
  938.     while (Prenum > 0) {
  939.         c = gchar(Curschar);
  940.         if (isalpha(c)) {
  941.         if (islower(c))
  942.             pchar(Curschar, toupper(c));
  943.         else
  944.             pchar(Curschar, tolower(c));
  945.         }
  946.         if (!oneright())
  947.         break;
  948.         Prenum--;
  949.     }
  950.  
  951.     CHANGED;
  952.     updateline();
  953.     break;
  954.  
  955.       case UNDO_SHIFTJ:
  956.     CLEAROP;
  957.     if (UndoInProgress) {
  958.         if (dojoin(FALSE, FALSE))
  959.         updateNextscreen(VALID_TO_CURSCHAR);
  960.         break;
  961.     }
  962.     goto doSHIFTJcommand;
  963.  
  964.       case 'J':
  965.     CLEAROP;
  966. doSHIFTJcommand:
  967.     if (nextline(Curschar) == NULL) {    /* on last line */
  968.         beep();
  969.         break;
  970.     }
  971.     ResetBuffers();
  972.  
  973.     temp_Curschar = *Curschar;
  974.     nn = strlen(Curschar->linep->s);
  975.     if (nn < 0)
  976.         nn = 0;
  977.     n = RowNumber(&temp_Curschar);
  978.  
  979.     AppendToRedobuff("J");
  980.  
  981.     AppendPositionToUndobuff(nn, n);
  982.  
  983.     AppendPositionToUndoUndobuff(0, n);
  984.     AppendToUndoUndobuff("J");
  985.  
  986.     if (linewhite(nextline(Curschar))) {
  987.         AppendToUndobuff("a\n");
  988.         if (!dojoin(FALSE, TRUE)) {
  989.         beep();
  990.         break;
  991.         }
  992.     } else if (lineempty(Curschar)) {
  993.         AppendToUndobuff("i\n");
  994.         if (!dojoin(FALSE, TRUE)) {
  995.         beep();
  996.         break;
  997.         }
  998.     } else {
  999.         AppendToUndobuff("dli\n");
  1000.         if (!dojoin(TRUE, TRUE)) {
  1001.         beep();
  1002.         break;
  1003.         }
  1004.     }
  1005.  
  1006.     AppendToUndobuff(ESC_STR);
  1007.     AppendPositionToUndobuff(nn, n);
  1008.  
  1009.     updateNextscreen(VALID_TO_CURSCHAR);
  1010.     break;
  1011.  
  1012.       case K_CGRAVE:        /* shorthand command */
  1013.     CLEAROP;
  1014.     stuffReadbuff(":e #\n");
  1015.     break;
  1016.  
  1017.       case 'Z':        /* write, if changed, and exit */
  1018.     if (vgetc() != 'Z') {
  1019.         beep();
  1020.         break;
  1021.     }
  1022.     if (Changed) {
  1023.         if (Filename != NULL) {
  1024.         if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL))
  1025.             return;
  1026.         } else {
  1027.         emsg("No output file");
  1028.         return;
  1029.         }
  1030.     }
  1031.     getout(0);
  1032.     break;
  1033.  
  1034.       case '.':
  1035.     CLEAROP;
  1036.     if (Redobuffptr != NULL) {
  1037.         stuffReadbuff(Redobuff);
  1038.  
  1039.         stuffReadbuff(ENABLE_REDRAWING_STR);
  1040.         RedrawingDisabled = TRUE;
  1041.     } else
  1042.         beep();
  1043.     break;
  1044.  
  1045.       case 'u':
  1046.       case K_UNDO:
  1047.     CLEAROP;
  1048.     if (UndoInProgress) {
  1049.         p = UndoUndobuff;
  1050.         UndoUndobuff = Undobuff;
  1051.         Undobuff = p;
  1052.         p = UndoUndobuffptr;
  1053.         UndoUndobuffptr = Undobuffptr;
  1054.         Undobuffptr = p;
  1055.  
  1056.         UndoInProgress = FALSE;
  1057.         RedrawingDisabled = FALSE;
  1058.         updateNextscreen(NOT_VALID);
  1059.     } else if (Undobuffptr != NULL) {
  1060.         stuffReadbuff(Undobuff);
  1061.         stuffReadbuff("u");
  1062.         UndoInProgress = TRUE;
  1063.         RedrawingDisabled = TRUE;
  1064.     } else {
  1065.         beep();
  1066.     }
  1067.     break;
  1068.  
  1069.       default:
  1070.     CLEAROP;
  1071.     beep();
  1072.     break;
  1073.     }
  1074.  
  1075.     /*
  1076.      * If an operation is pending, handle it... 
  1077.      */
  1078.     if (finish_op) {        /* we just finished an operator */
  1079.     if (operator == NOP)    /* ... but it was cancelled */
  1080.         return;
  1081.  
  1082.     switch (operator) {
  1083.  
  1084.       case LSHIFT:
  1085.       case RSHIFT:
  1086.         ResetBuffers();
  1087.  
  1088.         n = RowNumber(&startop);
  1089.         AppendPositionToUndobuff(startop.index, n);
  1090.         AppendPositionToUndoUndobuff(startop.index, n);
  1091.         if (Prenum != 0) {
  1092.         AppendNumberToRedobuff(Prenum);
  1093.         AppendNumberToUndobuff(Prenum);
  1094.         AppendNumberToUndoUndobuff(Prenum);
  1095.         }
  1096.         AppendToRedobuff((operator == LSHIFT) ? "<" : ">");
  1097.         AppendToUndobuff((operator == LSHIFT) ? ">" : "<");
  1098.         AppendToUndoUndobuff((operator == LSHIFT) ? "<" : ">");
  1099.         AppendToRedobuff(mkstr(c));
  1100.         if (c == '>')
  1101.         AppendToUndobuff("<");
  1102.         else if (c == '<')
  1103.         AppendToUndobuff(">");
  1104.         else
  1105.         AppendToUndobuff(mkstr(c));
  1106.         AppendToUndoUndobuff(mkstr(c));
  1107.  
  1108.         doshift(operator);
  1109.         break;
  1110.  
  1111.       case DELETE:
  1112.         ResetBuffers();
  1113.  
  1114.         n = RowNumber(&startop);
  1115.         AppendPositionToUndoUndobuff(startop.index, n);
  1116.  
  1117.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1118.         n = RowNumber(&temp_Curschar);
  1119.         if (Prenum != 0) {
  1120.         AppendNumberToRedobuff(Prenum);
  1121.         AppendNumberToUndoUndobuff(Prenum);
  1122.         }
  1123.         AppendToRedobuff("d");
  1124.         AppendToUndoUndobuff("d");
  1125.         AppendToRedobuff(mkstr(c));
  1126.         AppendToUndoUndobuff(mkstr(c));
  1127.         if (nchar != NUL) {
  1128.         AppendToRedobuff(mkstr(nchar));
  1129.         AppendToUndoUndobuff(mkstr(nchar));
  1130.         }
  1131.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1132.  
  1133.         dodelete(TRUE, !UndoInProgress, !UndoInProgress);
  1134.  
  1135.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1136.         break;
  1137.  
  1138.       case YANK:
  1139.         ResetBuffers();    /* no redo/undo/(undo of undo) on yank... */
  1140.         doyank();
  1141.         if (!ybcrossline)
  1142.         *Curschar = startop;
  1143.         else if (lt(&startop, Curschar))
  1144.         *Curschar = startop;
  1145.         break;
  1146.  
  1147.       case CHANGE:
  1148.         ResetBuffers();
  1149.  
  1150.         n = RowNumber(&startop);
  1151.         AppendPositionToUndoUndobuff(startop.index, n);
  1152.  
  1153.         temp_Curschar = (lt(&startop, Curschar) ? startop : *Curschar);
  1154.         n = RowNumber(&temp_Curschar);
  1155.         if (mtype == MLINE)
  1156.         AppendPositionToUndobuff(0, n);
  1157.         else
  1158.         AppendPositionToUndobuff(temp_Curschar.index, n);
  1159.  
  1160.         if (Prenum != 0) {
  1161.         AppendNumberToRedobuff(Prenum);
  1162.         AppendNumberToUndoUndobuff(Prenum);
  1163.         }
  1164.         AppendToRedobuff("c");
  1165.         AppendToUndoUndobuff("c");
  1166.         AppendToRedobuff(mkstr(c));
  1167.         AppendToUndoUndobuff(mkstr(c));
  1168.         if (nchar != NUL) {
  1169.         AppendToRedobuff(mkstr(nchar));
  1170.         AppendToUndoUndobuff(mkstr(nchar));
  1171.         }
  1172.         dochange();
  1173.  
  1174.         last_command = 'c';
  1175.         break;
  1176.  
  1177.       default:
  1178.         beep();
  1179.     }
  1180.     operator = NOP;
  1181.     }
  1182. }
  1183.  
  1184. /*
  1185.  * tabinout(shift_type, num) 
  1186.  *
  1187.  * If shift_type == RSHIFT, add a tab to the begining of the next num lines;
  1188.  * otherwise delete a tab from the beginning of the next num lines. 
  1189.  */
  1190. static void
  1191. tabinout(shift_type, num)
  1192.     int             shift_type;
  1193.     int             num;
  1194. {
  1195.     LPtr           *p;
  1196.  
  1197.     beginline(FALSE);
  1198.     while (num-- > 0) {
  1199.     beginline(FALSE);
  1200.     if (shift_type == RSHIFT)
  1201.         inschar(TAB);
  1202.     else {
  1203.         if (gchar(Curschar) == TAB)
  1204.         delchar(TRUE, FALSE);
  1205.     }
  1206.     if (num > 0) {
  1207.         if ((p = nextline(Curschar)) != NULL)
  1208.         *Curschar = *p;
  1209.         else
  1210.         break;
  1211.     }
  1212.     }
  1213. }
  1214.  
  1215. /*
  1216.  * doshift - handle a shift operation 
  1217.  */
  1218. static void
  1219. doshift(op)
  1220.     int             op;
  1221. {
  1222.     LPtr            top, bot;
  1223.     int             nlines;
  1224.  
  1225.     top = startop;
  1226.     bot = *Curschar;
  1227.  
  1228.     if (lt(&bot, &top))
  1229.     pswap(top, bot);
  1230.  
  1231.     nlines = cntllines(&top, &bot);
  1232.     *Curschar = top;
  1233.     tabinout(op, nlines);
  1234.  
  1235.     /*
  1236.      * The cursor position afterward is the prior of the two positions. 
  1237.      */
  1238.     *Curschar = top;
  1239.  
  1240.     /*
  1241.      * If we were on the last char of a line that got shifted left, then move
  1242.      * left one so we aren't beyond the end of the line 
  1243.      */
  1244.     if (gchar(Curschar) == NUL && Curschar->index > 0)
  1245.     Curschar->index--;
  1246.  
  1247.     if (op == RSHIFT)
  1248.     oneright();
  1249.     else
  1250.     oneleft();
  1251.  
  1252.     updateNextscreen(NOT_VALID);
  1253.  
  1254.     if (nlines > P(P_RP))
  1255.     smsg("%d lines %ced", nlines, (op == RSHIFT) ? '>' : '<');
  1256. }
  1257.  
  1258. /*
  1259.  * dodelete - handle a delete operation 
  1260.  */
  1261. static void
  1262. dodelete(redraw, setup_for_undo, try_to_yank)
  1263.     bool_t          redraw;
  1264.     bool_t          setup_for_undo;
  1265.     bool_t          try_to_yank;
  1266. {
  1267.     LPtr            top, bot;
  1268.     int             nlines;
  1269.     int             n;
  1270.  
  1271.     /*
  1272.      * Do a yank of whatever we're about to delete. If there's too much stuff
  1273.      * to fit in the yank buffer, then get a confirmation before doing the
  1274.      * delete. This is crude, but simple. And it avoids doing a delete of
  1275.      * something we can't put back if we want. 
  1276.      */
  1277.     if (try_to_yank) {
  1278.     if (!doyank()) {
  1279.         msg("yank buffer exceeded: press <y> to confirm");
  1280.         if (vgetc() != 'y') {
  1281.         emsg("delete aborted");
  1282.         *Curschar = startop;
  1283.         return;
  1284.         }
  1285.     }
  1286.     }
  1287.     top = startop;
  1288.     bot = *Curschar;
  1289.  
  1290.     if (lt(&bot, &top))
  1291.     pswap(top, bot);
  1292.  
  1293.     *Curschar = top;
  1294.     nlines = cntllines(&top, &bot);
  1295.     cursupdate();
  1296.  
  1297.     if (mtype == MLINE) {
  1298.     if (operator == CHANGE) {
  1299.         last_command_char = 'a';
  1300.         delline(nlines - 1, TRUE);
  1301.         Curschar->index = 0;
  1302.         while (delchar(TRUE, FALSE));
  1303.     } else {
  1304.         if ((Filetop->linep->next == top.linep) &&
  1305.         (bot.linep->next == Fileend->linep))
  1306.         last_command_char = 'a';
  1307.         else if (bot.linep->next == Fileend->linep)
  1308.         last_command_char = 'o';
  1309.         else
  1310.         last_command_char = 'O';
  1311.         if (setup_for_undo)
  1312.         AppendToUndobuff(mkstr(last_command_char));
  1313.         delline(nlines, TRUE);
  1314.     }
  1315.     } else if (top.linep == bot.linep) {    /* del. within line */
  1316.     if (!mincl)
  1317.         dec(&bot);
  1318.  
  1319.     if (endofline(&bot))
  1320.         last_command_char = 'a';
  1321.     else
  1322.         last_command_char = 'i';
  1323.     if (setup_for_undo)
  1324.         AppendToUndobuff(mkstr(last_command_char));
  1325.     n = bot.index - top.index + 1;
  1326.     while (n--)
  1327.         if (!delchar(TRUE, FALSE))
  1328.         break;
  1329.     } else {            /* del. between lines */
  1330.     if (endofline(&top)) {
  1331.         if (nextline(&top)) {
  1332.         if (lineempty(nextline(&top)))
  1333.             last_command_char = 'a';
  1334.         else
  1335.             last_command_char = 'i';
  1336.         } else {
  1337.         last_command_char = 'a';
  1338.         }
  1339.     } else {
  1340.         last_command_char = 'i';
  1341.     }
  1342.     if (setup_for_undo)
  1343.         AppendToUndobuff(mkstr(last_command_char));
  1344.  
  1345.     n = Curschar->index;
  1346.     while (Curschar->index >= n)
  1347.         if (!delchar(TRUE, FALSE))
  1348.         break;
  1349.  
  1350.     top = *Curschar;
  1351.     *Curschar = *nextline(Curschar);
  1352.     delline(nlines - 2, TRUE);
  1353.     Curschar->index = 0;
  1354.     n = bot.index;
  1355.     if (!mincl)
  1356.         n--;
  1357.  
  1358.     while (n-- >= 0)
  1359.         if (!delchar(TRUE, FALSE))
  1360.         break;
  1361.     *Curschar = top;
  1362.     dojoin(FALSE, FALSE);
  1363.     }
  1364.  
  1365.     if (mtype == MCHAR && nlines == 1 && redraw && P(P_NU) == FALSE)
  1366.     updateline();
  1367.     else
  1368.     updateNextscreen(NOT_VALID);
  1369.  
  1370.     if (nlines > P(P_RP))
  1371.     smsg("%d fewer lines", nlines);
  1372.  
  1373.     if (setup_for_undo) {
  1374.     AppendToUndobuff(Yankbuff);
  1375.     AppendToUndobuff(ESC_STR);
  1376.     }
  1377. }
  1378.  
  1379. /*
  1380.  * dochange - handle a change operation 
  1381.  */
  1382. static void
  1383. dochange()
  1384. {
  1385.     LPtr            l;
  1386.  
  1387.     if (lt(Curschar, &startop))
  1388.     l = *Curschar;
  1389.     else
  1390.     l = startop;
  1391.  
  1392.     dodelete(FALSE, FALSE, !UndoInProgress);
  1393.  
  1394.     if ((l.index > Curschar->index) && !lineempty(Curschar))
  1395.     inc(Curschar);
  1396.  
  1397.     startinsert(FALSE);
  1398. }
  1399.  
  1400. static          bool_t
  1401. doyank()
  1402. {
  1403.     LPtr            top, bot;
  1404.     char           *ybend = &Yankbuff[YANKSIZE - 1];
  1405.     int             nlines;
  1406.  
  1407.     Yankbuffptr = Yankbuff;
  1408.  
  1409.     top = startop;
  1410.     bot = *Curschar;
  1411.  
  1412.     if (lt(&bot, &top))
  1413.     pswap(top, bot);
  1414.  
  1415.     nlines = cntllines(&top, &bot);
  1416.  
  1417.     ybtype = mtype;        /* set the yank buffer type */
  1418.     ybcrossline = FALSE;
  1419.     if (LINEOF(&top) != LINEOF(&bot))
  1420.     ybcrossline = TRUE;
  1421.  
  1422.     if (mtype == MLINE) {
  1423.     ybcrossline = TRUE;
  1424.     top.index = 0;
  1425.     bot.index = strlen(bot.linep->s);
  1426.     /*
  1427.      * The following statement checks for the special case of yanking a
  1428.      * blank line at the beginning of the file. If not handled right, we
  1429.      * yank an extra char (a newline). 
  1430.      */
  1431.     if (dec(&bot) == -1) {
  1432.         *Yankbuff = NUL;
  1433.         Yankbuffptr = NULL;
  1434.         return TRUE;
  1435.     }
  1436.     } else {
  1437.     if (!mincl)
  1438.         if (!equal(&top, &bot))
  1439.         dec(&bot);
  1440.     }
  1441.  
  1442.     for (; ltoreq(&top, &bot); inc(&top)) {
  1443.     *Yankbuffptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
  1444.     Yankbuffptr++;
  1445.     if (Yankbuffptr >= ybend) {
  1446.         *Yankbuffptr = NUL;
  1447.         msg("yank too big for buffer");
  1448.         ybtype = MBAD;
  1449.         return FALSE;
  1450.     }
  1451.     }
  1452.  
  1453.     *Yankbuffptr = NUL;
  1454.  
  1455.     if (operator == YANK)
  1456.     if (nlines > P(P_RP))
  1457.         smsg("%d lines yanked", nlines);
  1458.  
  1459.     return TRUE;
  1460. }
  1461.  
  1462. static void
  1463. doput(dir)
  1464.     int             dir;
  1465. {
  1466.     bool_t          type;
  1467.  
  1468.     if (ybtype == MBAD) {
  1469.     beep();
  1470.     return;
  1471.     }
  1472.     type = (ybtype == MCHAR);
  1473.     if (dir == FORWARD)
  1474.     stuffReadbuff(type ? "a" : "o");
  1475.     else
  1476.     stuffReadbuff(type ? "i" : "O");
  1477.  
  1478.     stuffReadbuff(Yankbuff);
  1479.     stuffReadbuff(ESC_STR);
  1480.  
  1481.     if (ybtype != MCHAR)
  1482.     stuffReadbuff("^");
  1483. }
  1484.  
  1485. static void
  1486. startinsert(startln)
  1487.     int             startln;    /* if set, insert at start of line */
  1488. {
  1489.     *Insstart = *Curschar;
  1490.     if (startln) {
  1491.     Insstart->index = 0;
  1492.     }
  1493.     *Insbuff = NUL;
  1494.     Insbuffptr = NULL;
  1495.  
  1496.     State = INSERT;
  1497.     if (P(P_MO))
  1498.     msg("Insert Mode");
  1499. }
  1500.  
  1501. void
  1502. ResetBuffers()
  1503. {
  1504.     if (UndoInProgress)
  1505.     return;
  1506.  
  1507.     *Redobuff = NUL;
  1508.     Redobuffptr = NULL;
  1509.  
  1510.     *Undobuff = NUL;
  1511.     Undobuffptr = NULL;
  1512.  
  1513.     *UndoUndobuff = NUL;
  1514.     UndoUndobuffptr = NULL;
  1515. }
  1516.  
  1517. void
  1518. AppendToInsbuff(s)
  1519.     char           *s;
  1520. {
  1521.     if (UndoInProgress)
  1522.     return;
  1523.  
  1524.     if (Insbuffptr == NULL) {
  1525.     if ((strlen(s) + 1) < INSERT_SIZE) {
  1526.         strcpy(Insbuff, s);
  1527.         Insbuffptr = Insbuff;
  1528.         return;
  1529.     }
  1530.     } else if ((strlen(Insbuff) + strlen(s) + 1) < INSERT_SIZE) {
  1531.     strcat(Insbuff, s);
  1532.     return;
  1533.     }
  1534.     emsg("Couldn't AppendToInsbuff() - clearing Insbuff\n");
  1535.     *Insbuff = NUL;
  1536.     Insbuffptr = NULL;
  1537. }
  1538.  
  1539. void
  1540. AppendToRedobuff(s)
  1541.     char           *s;
  1542. {
  1543.     if (UndoInProgress)
  1544.     return;
  1545.  
  1546.     if (Redobuffptr == (char *) (-2)) {
  1547.     return;
  1548.     }
  1549.     if (Redobuffptr == (char *) (-1)) {
  1550.         Redobuffptr = (char *) (-2);
  1551.     emsg("Couldn't AppendToRedobuff() - Redobuff corrupt");
  1552.     return;
  1553.     }
  1554.     if (Redobuffptr == NULL) {
  1555.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1556.         strcpy(Redobuff, s);
  1557.         Redobuffptr = Redobuff;
  1558.         return;
  1559.     }
  1560.     } else if ((strlen(Redobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1561.     strcat(Redobuff, s);
  1562.     return;
  1563.     }
  1564.     emsg("Couldn't AppendToRedobuff() - clearing Redobuff");
  1565.     *Redobuff = NUL;
  1566.     Redobuffptr = (char *) (-1);
  1567. }
  1568.  
  1569. void
  1570. AppendNumberToRedobuff(n)
  1571.     int             n;
  1572. {
  1573.     char            buf[32];
  1574.  
  1575.     if (UndoInProgress)
  1576.     return;
  1577.  
  1578.     sprintf(buf, "%d", n);
  1579.     AppendToRedobuff(buf);
  1580. }
  1581.  
  1582. void
  1583. AppendToUndobuff(s)
  1584.     char           *s;
  1585. {
  1586.     if (UndoInProgress)
  1587.     return;
  1588.  
  1589.     if (Undobuffptr == (char *) (-2)) {
  1590.     return;
  1591.     }
  1592.     if (Undobuffptr == (char *) (-1)) {
  1593.         Undobuffptr = (char *) (-2);
  1594.     emsg("Couldn't AppendToUndobuff() - Undobuff corrupt");
  1595.     return;
  1596.     }
  1597.     if (Undobuffptr == NULL) {
  1598.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1599.         strcpy(Undobuff, s);
  1600.         Undobuffptr = Undobuff;
  1601.         return;
  1602.     }
  1603.     } else if ((strlen(Undobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1604.     strcat(Undobuff, s);
  1605.     return;
  1606.     }
  1607.     emsg("Couldn't AppendToUndobuff() - clearing Undobuff");
  1608.     *Undobuff = NUL;
  1609.     Undobuffptr = (char *) (-1);
  1610. }
  1611.  
  1612. void
  1613. AppendNumberToUndobuff(n)
  1614.     int             n;
  1615. {
  1616.     char            buf[32];
  1617.  
  1618.     if (UndoInProgress)
  1619.     return;
  1620.  
  1621.     sprintf(buf, "%d", n);
  1622.     AppendToUndobuff(buf);
  1623. }
  1624.  
  1625. void
  1626. AppendPositionToUndobuff(column, row)
  1627.     int             column;
  1628.     int             row;
  1629. {
  1630.     if (UndoInProgress)
  1631.     return;
  1632.  
  1633.     AppendNumberToUndobuff(row);
  1634.     AppendToUndobuff("G");
  1635.     AppendNumberToUndobuff(column);
  1636.     if (column)
  1637.     AppendToUndobuff("l");
  1638. }
  1639.  
  1640. void
  1641. AppendToUndoUndobuff(s)
  1642.     char           *s;
  1643. {
  1644.     if (UndoInProgress)
  1645.     return;
  1646.  
  1647.     if (UndoUndobuffptr == (char *) (-2)) {
  1648.     return;
  1649.     }
  1650.     if (UndoUndobuffptr == (char *) (-1)) {
  1651.         UndoUndobuffptr = (char *) (-2);
  1652.     emsg("Couldn't AppendToUndoUndobuff() - UndoUndobuff corrupt");
  1653.     return;
  1654.     }
  1655.     if (UndoUndobuffptr == NULL) {
  1656.     if ((strlen(s) + 1) < REDO_UNDO_SIZE) {
  1657.         strcpy(UndoUndobuff, s);
  1658.         UndoUndobuffptr = Undobuff;
  1659.         return;
  1660.     }
  1661.     } else if ((strlen(UndoUndobuff) + strlen(s) + 1) < REDO_UNDO_SIZE) {
  1662.     strcat(UndoUndobuff, s);
  1663.     return;
  1664.     }
  1665.     emsg("Couldn't AppendToUndoUndobuff() - clearing UndoUndobuff");
  1666.     *UndoUndobuff = NUL;
  1667.     UndoUndobuffptr = (char *) (-1);
  1668. }
  1669.  
  1670. void
  1671. AppendNumberToUndoUndobuff(n)
  1672.     int             n;
  1673. {
  1674.     char            buf[32];
  1675.  
  1676.     if (UndoInProgress)
  1677.     return;
  1678.  
  1679.     sprintf(buf, "%d", n);
  1680.     AppendToUndoUndobuff(buf);
  1681. }
  1682.  
  1683. void
  1684. AppendPositionToUndoUndobuff(column, row)
  1685.     int             column;
  1686.     int             row;
  1687. {
  1688.     if (UndoInProgress)
  1689.     return;
  1690.  
  1691.     AppendNumberToUndoUndobuff(row);
  1692.     AppendToUndoUndobuff("G");
  1693.     AppendNumberToUndoUndobuff(column);
  1694.     if (column)
  1695.     AppendToUndoUndobuff("l");
  1696. }
  1697.  
  1698. static          bool_t
  1699. dojoin(leading_space, strip_leading_spaces)
  1700.     bool_t          leading_space;
  1701.     bool_t          strip_leading_spaces;
  1702. {
  1703.     int             scol;    /* save cursor column */
  1704.     int             currsize;    /* size of the current line */
  1705.     int             nextsize;    /* size of the next line */
  1706.  
  1707.     if (nextline(Curschar) == NULL)    /* on last line */
  1708.     return FALSE;
  1709.  
  1710.     if (!canincrease(nextsize = strlen(Curschar->linep->next->s)))
  1711.     return FALSE;
  1712.  
  1713.     currsize = strlen(Curschar->linep->s);
  1714.  
  1715.     while (oneright());        /* to end of line */
  1716.  
  1717.     strcat(Curschar->linep->s, Curschar->linep->next->s);
  1718.  
  1719.     /*
  1720.      * Delete the following line. To do this we move the cursor there
  1721.      * briefly, and then move it back. Don't back up if the delete made us
  1722.      * the last line. 
  1723.      */
  1724.     Curschar->linep = Curschar->linep->next;
  1725.     scol = Curschar->index;
  1726.  
  1727.     if (nextline(Curschar) != NULL) {
  1728.     delline(1, TRUE);
  1729.     Curschar->linep = Curschar->linep->prev;
  1730.     } else
  1731.     delline(1, TRUE);
  1732.  
  1733.     Curschar->index = scol;
  1734.  
  1735.     if (currsize)
  1736.     oneright();        /* go to first char. of joined line */
  1737.  
  1738.     if (nextsize != 0 && strip_leading_spaces) {
  1739.     /*
  1740.      * Delete leading white space on the joined line and insert a single
  1741.      * space. 
  1742.      */
  1743.     while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB) {
  1744.         delchar(TRUE, TRUE);
  1745.     }
  1746.     if (leading_space)
  1747.         inschar(' ');
  1748.     }
  1749.     CHANGED;
  1750.  
  1751.     return TRUE;
  1752. }
  1753.  
  1754. /*
  1755.  * linewhite() - returns TRUE if the line consists only of white space
  1756.  */
  1757.  
  1758. bool_t
  1759. linewhite(p)
  1760.     LPtr           *p;
  1761. {
  1762.     register int    i;
  1763.     register char   c;
  1764.  
  1765.     i = 1;
  1766.     c = p->linep->s[0];
  1767.     while (c != NUL) {
  1768.     if (c != ' ' && c != '\t')
  1769.         return (FALSE);
  1770.     c = p->linep->s[i++];
  1771.     }
  1772.  
  1773.     return (TRUE);
  1774. }
  1775.